package com.baitiaojun.tenant.loader.impl;

import com.baitiaojun.common.enums.impl.ServerExpEnums;
import com.baitiaojun.common.exception.ServerException;
import com.baitiaojun.common.utils.id.ShortUUID;
import com.baitiaojun.common.utils.spring.SpringUtils;
import com.baitiaojun.common.utils.string.StringUtils;
import com.baitiaojun.tenant.dto.TenantDTO;
import com.baitiaojun.tenant.handler.ScriptHandler;
import com.baitiaojun.tenant.loader.DataSourceLoader;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

@Slf4j
public class MySQLDatasourceLoader implements DataSourceLoader {

    /**
     * 初始化数据库
     * @param tenantDTO
     */
    @Override
    public String initDB(TenantDTO tenantDTO) {
        log.info("开始执行租户数据库初始化");
        //获取数据库连接
        Connection connection = getConnection(tenantDTO);
        Statement statement = null;
        String dbName = null;
        try {
            //创建连接
            statement = connection.createStatement();
            //创建数据库
            String prefix = SpringUtils.getEnvironment().getProperty("tenant.db.prefix");
            dbName = getDBName(prefix);
            createDB(statement, dbName);
            //执行授权
            grant(statement, tenantDTO.getUsername(), tenantDTO.getPassword());
            //切换到目标数据库
            connection.setCatalog(dbName);
            log.info("当前目标数据库：{}", connection.getCatalog());
            //开启手动事物
            connection.setAutoCommit(false);
            //初始化脚本
            ScriptHandler.script(connection, tenantDTO.getTenant(), tenantDTO.getAdminName(), tenantDTO.getAdminPassword());
            log.info("执行租户数据库初始化成功");
            return dbName;
        } catch (Exception e) {
            e.printStackTrace();
            if (statement != null && StringUtils.isNotEmpty(dbName)) {
                try {
                    statement.execute("DROP DATABASE IF EXISTS " + dbName);
                } catch (SQLException ex) {
                    throw new ServerException(ServerExpEnums.EXP_DROP_DB_ERROR);
                }
            }
            throw new ServerException(ServerExpEnums.EXP_EXECUTE_DB_ERROR);
        } finally {
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    throw new ServerException(ServerExpEnums.EXP_BAD_CLOSE_STATEMENT);
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    throw new ServerException(ServerExpEnums.EXP_BAD_CLOSE_CONNECTION);
                }
            }
        }
    }

    /**
     * 获取数据库连接
     * @param tenantDB
     * @return
     */
    @Override
    public Connection getConnection(TenantDTO tenantDB) {
        try {
            return DriverManager.getConnection(tenantDB.getUrl(), tenantDB.getUsername(), tenantDB.getPassword());
        } catch (SQLException e) {
            throw new ServerException(ServerExpEnums.EXP_BAD_DB_CONNECTION);
        }
    }

    private void createDB(Statement statement, String dbName) throws SQLException {
        String createDB = "CREATE DATABASE IF NOT EXISTS `" + dbName + "` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;";
        statement.execute(createDB);
    }

    private String getDBName(String dbPrefix) {
        return dbPrefix + ShortUUID.nextID();
    }

    private void grant(Statement statement, String username, String password) throws SQLException {
        String flush = "FLUSH PRIVILEGES;";
        String dropUser = "DROP USER '" + username + "'@'%';";
        statement.execute(dropUser);
        statement.execute(flush);
        String createUser = "CREATE USER '" + username + "'@'%' IDENTIFIED BY '" + password + "';";
        statement.execute(createUser);
        String grant = "GRANT ALL PRIVILEGES ON *.* TO '" + username + "'@'%' WITH GRANT OPTION;";
        statement.execute(grant);
        statement.execute(flush);
    }
}
